home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48_2 / mpe_v1_0.src < prev    next >
Internet Message Format  |  1995-03-31  |  52KB

  1. From comp.sys.handhelds Fri May 24 22:33:29 1991
  2. Path: seq!ecsgate!mcnc!gatech!ncar!elroy.jpl.nasa.gov!decwrl!deccrl!news.crl.dec.com!nntpd.lkg.dec.com!ryn.mro4.dec.com!pinbot.enet.dec.com!ervin
  3. From: ervin@pinbot.enet.dec.com (Joseph James Ervin)
  4. Newsgroups: comp.sys.handhelds
  5. Subject: Repost of MPE V1.0 Source code.  (oops)
  6. Message-ID: <4846@ryn.mro4.dec.com>
  7. Date: 15 May 91 20:10:01 GMT
  8. Sender: guest@ryn.mro4.dec.com
  9. Reply-To: ervin@pinbot.enet.dec.com (Joseph James Ervin)
  10. Organization: Digital Equipment Corporation
  11. Lines: 1517
  12.  
  13. The initial post of this source code has a bug in the GET_CUR_TIME routine.
  14.  
  15. This post is the one I should have sent out the first time.  Sorry for the
  16. inconvenience.
  17.  
  18. >>>Joe Ervin
  19.  
  20. ****************************************CUT HERE*******************************
  21. ; MPE V1.0.  Copyright Joseph James Ervin 1991.
  22.  
  23. ; The following is a simple multiprogramming environment (MPE) which allows
  24. ; users to program concurrent processes to run on the HP48SX.  
  25.  
  26. ; Addresses of Rom routines
  27.  
  28.         save_registers  =       ^x679B  ; Saves system registers. Uses C, D0.
  29.         restore_registers =     ^x67d2  ; Restores system register.
  30.         rplcont         =       ^x71BE  ; Jumps to the next RPL instruction.
  31.         rr_rplcont      =       ^x5143  ; Restores the system registers and
  32.                                         ; then does rplcont.
  33.         get_short       =       ^x6641  ; Pops the short of TOS and returns it
  34.                                         ; into A.A
  35.         push_a_cont     =       ^x357C  ; Push the contents of A.A onto stack
  36.                                         ; and continue RPL.
  37.         push_r0_short   =       ^x6537  ; Push R0 as new system binary.  This
  38.                                         ; routine uses the SAVED system
  39.     ROM_GET_TICKS    =     ^x130E    ; Returns the number of timer ticks
  40.                     ; in C.13.  Trashes just about every
  41.                         ; other register.
  42.         radix ^d10
  43.     
  44. ; General address definitions
  45.     bos        =     ^x7057E    ; Pointer to beginning of the stack.
  46.     pict_pointer    =    ^x70565    ; Pointer to PICT grob.
  47.         menu_pointer    =     ^x70556    ; Pointer to Stack Grob.
  48.     keybuf        =     ^x704ea ; keyboard buffer.
  49.  
  50. ; ***************************************************************************
  51. ; The following macro is used to generate a process header and to allocate 
  52. ; storage for the process context.  The macro  requires as its only parameter 
  53. ; the ID of the process for which the process header is to be generated. 
  54.  
  55.     macro   gen_headers n=0, label
  56.     save n
  57.         n = $n
  58.         if (n==0)   ; This is for the scheduler.
  59. $(label)_context:
  60.           DATA.W  0, 0            ; Storage for B, D.
  61.           DATA.A  0, 0            ; Storage for D0, D1.
  62.           DATA.W  0, 0, 0, 0, 0   ; Storage for R0, R1, R2, R3, R4
  63.  
  64. $(label)_header:     data.a 0, 0
  65.  
  66.     else  ; If not for scheduler.
  67.         if def process$(n)_init
  68.  
  69. ; The user got it right and defined the _init section of the
  70. ; process.  Now we need to allocate storage for the context area and build the
  71. ; process header.
  72.  
  73. $(label)_context:
  74.         DATA.W  0, 0            ; Storage for B, D.
  75.         DATA.A  0, 0            ; Storage for D0, D1.
  76.         DATA.W  0, 0, 0, 0, 0   ; Storage for R0, R1, R2, R3, R4
  77.  
  78. $(label)_header:    data.a 0, 0
  79.     endif
  80.         endif
  81.     restore n
  82.     endmacro
  83.     hide gen_headers                 
  84. ; ***************************************************************************
  85. ; The following macro is used to generate the process descriptor list used
  86. ; by the scheduler.  This macro checks to see that the processes declared by
  87. ; the user have contiguous process IDs.  If not, then an error message is 
  88. ; generated.
  89.  
  90.     macro gen_descriptor n
  91.     save n
  92.         n = $n
  93.     if (n == 0)
  94. DESCRIPTOR_LIST: DATA.A    scheduler_header    ; Process descriptors.
  95.       contig = 1
  96.       num_of_proc = 0
  97.     endif
  98.     if (!def process$(n)_init && !(n==0))
  99.       contig = 0
  100.     endif
  101.     if def process$(n)_init
  102.       num_of_proc = (num_of_proc + 1)
  103.       if (contig==0)
  104.         error Defined processes are not contiguously numbered.
  105.       endif
  106.     
  107.  
  108. ; The user got it right and defined both the _init and _code sections of the
  109. ; process.  The process context storage and header should already have been 
  110. ; set up, so now we need to add the entry for the process descriptor.
  111.  
  112.       DATA.A    process$(n)_header    ; Process descriptor.
  113.     endif
  114.  
  115.     restore n
  116.     endmacro
  117.     hide gen_descriptor                 
  118.  
  119.  
  120.  
  121.  
  122. ; ***************************************************************************
  123. ; The following macro is used to make the relative addresses stored into the
  124. ; process header into absolute addresses.  
  125.  
  126.  
  127.     macro header_init n
  128.     save n, dest, src1, src2
  129.         n = $n
  130.     if ((def process$(n)_init) || (n==0))
  131.  
  132. ; The user has defined this process, so we need to write the address pointers
  133. ; in the process header. 
  134.  
  135.       if (n==0)
  136.         dest = scheduler_header
  137.         src1 = scheduler_context
  138.         src2 = scheduler_code
  139.       else
  140.         dest = process$(n)_header
  141.         src1 = process$(n)_context
  142.         src2 = process$(n)_init
  143.       endif
  144.  
  145.       addr    $dest, D0    ; Point D0 to the process (scheduler) header.
  146.       addr    $src1, c    ; Get address of context.
  147.       move.a    c, @d0
  148.       addr    $src2, c
  149.       add.a    ^d5, d0
  150.       move.a    c, @d0
  151.  
  152.     
  153.     endif    
  154.         restore n, dest, src1, src2
  155.     endmacro
  156.     hide header_init
  157.  
  158. ; ***************************************************************************
  159.  
  160.     macro    fill    n
  161.     save n
  162.     n = $n
  163.  
  164.     if def process$(n)_init
  165.     addr    process$(n)_header, c
  166.     move.a    c, @d0    ; Fill in the process descriptor.
  167.     add.a    5, d0    ; D0 now points to the next process descriptor.
  168.     endif
  169.  
  170.  
  171.     restore    n
  172.     endmacro
  173.     hide fill    
  174.  
  175. ; The following macro writes the address pointers in the process descriptor
  176. ; list.
  177.  
  178.     macro descriptor_init
  179.  
  180.     ; We know that the scheduler process exists, so we can fill in that
  181.     ; descriptor.
  182.     
  183.     addr    descriptor_list, d0    ; First, point d0 at the descriptors.
  184.     addr    scheduler_header, c    ; Get the scheduler descriptor.
  185.     move.a    c, @d0            ; Fill in scheduler descriptor.
  186.     add.a    5, d0    ; D0 now points to the next process descriptor.
  187.  
  188. ; Now we want to sequence through the processes.  For each process, if the 
  189. ; user has defined the process, then we need to fill in the descriptor.
  190.  
  191.     fill 1     ; fill in descriptors for the 20 processes.
  192.     fill 2
  193.     fill 3
  194.     fill 4
  195.     fill 5
  196.     fill 6
  197.     fill 7
  198.     fill 8
  199.     fill 9
  200.     fill 10
  201.     fill 11
  202.     fill 12
  203.     fill 13
  204.     fill 14
  205.     fill 15
  206.     fill 16
  207.     fill 17
  208.     fill 18
  209.     fill 19
  210.     fill 20
  211.  
  212.  
  213.         endmacro
  214.     hide descriptor_init
  215.  
  216. ; ****************************************************************************
  217. ; The following macro enables the user to easily configure a process to start
  218. ; at the INIT or CODE sections.  The first argument is an integer specifying
  219. ; the process to be reconfigured.  The second argument is the label of the new
  220. ; entry point. 
  221.  
  222.  
  223.     macro process_start    n, where
  224.     
  225.     addr    process$(n)_header, d0    ; Point d0 at the process header.
  226.     add.a    5, d0    ; Move down to the code pointer.
  227.     addr    $where, c
  228.     move.a    c, @d0        ; Reconfigures process "n" to run from "where"
  229.                 ; the next time it comes due.
  230.     endmacro
  231.     hide process_start
  232.  
  233. ; ****************************************************************************
  234. ; The following macro enables the user to easily configure a process to start
  235. ; at a new label.  This macro takes no arguments, but expects that C.A contains
  236. ; a pointer to the new entry point.  This macro then  
  237. ; automatically changes the entry point of the current process to 
  238. ; the address in C.A.  It is expected that the process would load C.A with the
  239. ; address label, possibly with the ADDR macro.  The purpose behind this is to 
  240. ; allow the programmer to easily redirect the entry point of current process 
  241. ; without requiring the use of the IF_PROC/ENDIF_PROC construct.  This is 
  242. ; particularly advantageous when many processes share a section of code.  
  243.  
  244.  
  245.     macro cur_process_start    
  246.     
  247.     PUSH.A     C    ; Save the pointer to the new entry point.
  248.     CALL    GET_CURRENT_ID    ; Get the process ID in A.B.  Trashes C.
  249.     CLR.W    C    ; 
  250.     MOVE.B    A, C    ;     
  251.     MOVE.W    C, A    ; Zero out upper bits of A.
  252.         ADD.A    C, C    ; 
  253.     ADD.A    C, C    ;
  254.     ADD.A    A, C    ; ...and multiply by 5.
  255.  
  256. ; C.A now contains the offset from beginning of the process descriptor list.
  257.  
  258.  
  259.     ADDR    DESCRIPTOR_LIST, D0 ; Address of descriptor list. Trashes A.
  260.     SWAP.A    A, D0
  261.     ADD.A    C, A    ; Add in the offset.  D0 points at process descriptor.
  262.     SWAP.A    A, D0
  263.     MOVE.A    @D0, C    ; Get the descriptor (pointer to the process header).
  264.     MOVE.A    C, D0    ; Point D0 to the process header.
  265.  
  266.     add.a    5, d0    ; Move down to the code pointer.
  267.     POP.A    C    ; Pop the new entry point back into C.
  268.     move.a    c, @d0    ; Reconfigures the current process to start at the
  269.             ; new entry point the next time it comes due.
  270.     endmacro
  271.     hide cur_process_start
  272.  
  273.  
  274. ;**************************************************************************
  275. ; The following macro is used by the MPE programmer to execute a certain piece
  276. ; of code conditioned on whether a specified process is current.
  277.  
  278.     macro    if_proc    n
  279.     n=$n
  280.              ; Do the macro.
  281.     save    nosym
  282.  
  283.     nosym = gensym
  284.  
  285.     swap.a    c, d0        ; Save D0 in C.A
  286.     addr    current_context_id, d0    ; Point to current context id variable.
  287.     move.b    @d0, a        ; Get the current context id.
  288.     swap.a    c, d0        ; Restore old D0.
  289.     move.p2    $(n), c        ; 
  290.     brne.b    c, a, $nosym    ; Skip the code if specified process is not
  291.                 ; current.
  292.     endmacro
  293.     hide if_proc    
  294.  
  295.     macro    endif_proc
  296. $NOSYM:    ; Execution jumps to here if the specified process was not current.
  297.  
  298.       restore  nosym
  299.     endmacro
  300.     hide    endif_proc
  301.  
  302.  
  303.  
  304.  
  305.     header
  306.  
  307.  
  308.     code
  309.  
  310.  
  311. ; Descriptions of global RAM routines    
  312.  
  313. ;    ADD_PROCESS    ; Arguments: A.A contains the address of the process
  314.             ;            header of the process to be added.
  315.             ;          C.W contains the execution time.
  316.                       
  317. ;    RUN_IT        ; Jumped-to from the SCHEDULER.  Runs the process 
  318.             ; whos ID is given in A.B.
  319.  
  320. ;     SAVE_CONTEXT    ; Saves the context of the current process, as
  321.             ; indicated by the CURRENT_CONTEXT_ID variable.
  322.  
  323. ;     RESTORE_CONTEXT    ; Restores the context of the process whos ID is given
  324.             ; in A.B.  This routine also leaves a pointer to the
  325.             ; code of the new process in C.A, so the calling
  326.             ; process can either jump to the new process, or just
  327.             ; use RESTORE_CONTEXT to copy the context of another
  328.             ; process to itself without actually transfering 
  329.             ; execution to the new process.
  330.  
  331.  
  332.         jump    START    ; Skip over all the global variables below and go
  333.                         ; straight into the initialization code.
  334.  
  335.  
  336. ; ****************************************************************************
  337. ; The following state variables are intended to be used with the 
  338. ; SAVE_STATE and RESTORE_STATE routines.  These routines save the system
  339. ; registers into the following global state variables.  A and C are never
  340. ; saved or restored.  SAVE_STATE trashes A and C.  RESTORE_STATE trashes A, but
  341. ; preserves C. 
  342.  
  343. STATE_DATA1:    DATA.W    0, 0 
  344.         DATA.5    0, 0    ; Scratch storage for B, D, D0, D1 
  345.                 ; This storage area can be used by each process
  346.                 ; whenever some scratch storage is needed. This
  347.                 ; storage is used by the SAVE_STATE and 
  348.                 ; RESTORE_STATE routines.
  349. ; ****************************************************************************
  350.  
  351.  
  352. ; ****************************************************************************
  353. ; The following variable is used to hold the global time variable. 
  354.  
  355. CUR_TIME:    DATA.W    0        ; Global variable to hold current time.
  356.                     ; Set by scheduler.  Read by processes.
  357. ; ***************************************************************************
  358.  
  359.  
  360. ; ***************************************************************************
  361. ; The following storage areas are to hold the register context for each 
  362. ; process running in the system.
  363.  
  364. CURRENT_CONTEXT_ID:    DATA.B    0 ; Indicates the ID of the processor context 
  365.                 ; we are currently in.  This variable will be 
  366.                 ; used by the SAVE_CONTEXT and RESTORE_CONTEXT
  367.                 ; routines. A zero identifies the scheduler.
  368.                 ; The other processes are numbered 1-255.
  369.  
  370.  
  371. ;*************************************************************************** 
  372. ; The following is the memory area where the schedulers run list is kept. 
  373. ; Each entry consists of a one BYTE process ID (1-255; 0 is reserved for the
  374. ; scheduler), followed by a 13 nibble time value which represents when this
  375. ; process should be run. The scheduler will step down this list, comparing the
  376. ; current time value stored in the CUR_TIME variable to the execution time of
  377. ; each process in the run list.  When a process comes due, the scheduler will
  378. ; call the SWAP_CONTEXT  routine which will swap in the context of the due
  379. ; process and then jump to the address given in the process header.
  380.  
  381.  
  382.     RUN_LIST_SIZE  = ^d10     ; There are 10 process slots.
  383. RUN_LIST:     DATA.B    0    ; ID of a process.
  384.         DATA.W    0    ; Execution time of process. 
  385.         DATA.B    0    ; ID of a process.
  386.         DATA.W    0    ; Execution time of process. 
  387.         DATA.B    0    ; ID of a process.
  388.         DATA.W    0    ; Execution time of process. 
  389.         DATA.B    0    ; ID of a process.
  390.         DATA.W    0    ; Execution time of process. 
  391.         DATA.B    0    ; ID of a process.
  392.         DATA.W    0    ; Execution time of process. 
  393.         DATA.B    0    ; ID of a process.
  394.         DATA.W    0    ; Execution time of process. 
  395.         DATA.B    0    ; ID of a process.
  396.         DATA.W    0    ; Execution time of process. 
  397.         DATA.B    0    ; ID of a process.
  398.         DATA.W    0    ; Execution time of process. 
  399.         DATA.B    0    ; ID of a process.
  400.         DATA.W    0    ; Execution time of process. 
  401.         DATA.B    0    ; ID of a process.
  402.         DATA.W    0    ; Execution time of process. 
  403.  
  404.  
  405.  
  406. ; *************************************************************************
  407. ; *************************************************************************
  408.  
  409.     gen_headers 0, scheduler    ; Create scheduler header.
  410.     gen_headers 1, process1    ; Create process headers.
  411.     gen_headers 2, process2
  412.     gen_headers 3, process3
  413.     gen_headers 4, process4
  414.     gen_headers 5, process5 
  415.     gen_headers 6, process6 
  416.     gen_headers 7, process7 
  417.     gen_headers 8, process8 
  418.     gen_headers 9, process9 
  419.     gen_headers 10, process10 
  420.     gen_headers 11, process11 
  421.     gen_headers 12, process12 
  422.     gen_headers 13, process13 
  423.     gen_headers 14, process14 
  424.     gen_headers 15, process15 
  425.     gen_headers 16, process16 
  426.     gen_headers 17, process17 
  427.     gen_headers 18, process18 
  428.     gen_headers 19, process19 
  429.     gen_headers 20, process20 
  430.  
  431.     gen_descriptor 0
  432.     gen_descriptor 1
  433.     gen_descriptor 2
  434.     gen_descriptor 3
  435.     gen_descriptor 4
  436.     gen_descriptor 5
  437.     gen_descriptor 6
  438.     gen_descriptor 7
  439.     gen_descriptor 8
  440.     gen_descriptor 9
  441.     gen_descriptor 10
  442.     gen_descriptor 11
  443.     gen_descriptor 12
  444.     gen_descriptor 13
  445.     gen_descriptor 14
  446.     gen_descriptor 15
  447.     gen_descriptor 16
  448.     gen_descriptor 17
  449.     gen_descriptor 18
  450.     gen_descriptor 19
  451.     gen_descriptor 20
  452.  
  453.  
  454. START:    
  455. ;    move.a    pc, a        ; debug
  456. ;    move.a    a, r0
  457. ;    call    save_registers
  458. ;    call    restore_registers
  459. ;    call    push_r0_short
  460. ;    call    save_registers
  461.  
  462.  
  463.     call    save_registers
  464.     header_init 0        ; Fill in the scheduler header.
  465.     header_init 1        ; Fill in the process headers.
  466.     header_init 2
  467.     header_init 3
  468.     header_init 4
  469.     header_init 5
  470.     header_init 6
  471.     header_init 7
  472.     header_init 8
  473.     header_init 9
  474.     header_init 10
  475.     header_init 11
  476.     header_init 12
  477.     header_init 13
  478.     header_init 14
  479.     header_init 15
  480.     header_init 16
  481.     header_init 17
  482.     header_init 18
  483.     header_init 19
  484.     header_init 20
  485.  
  486. ; Now fill in the address pointers in the process descriptor list.
  487.  
  488.     descriptor_init
  489.  
  490. ; Now we need to clear out the RUN_LIST so we know we are starting with
  491. ; a clean slate.
  492.  
  493.     move.p5    run_list_size, c    ; Number of entries.
  494.     addr    run_list, d0    ; Point at top of RUN_LIST. Trashes A.
  495.     CLR.W    A
  496. CLR_RUN_LIST:
  497.     MOVE.B    A, @D0
  498.     ADD.A    ^d2, d0
  499.     add.a    ^d16, d0
  500.     dec.a    c
  501.     brnz.a    c, clr_run_list    ; Clear out all the process IDs from RUN_LIST.
  502.     
  503.  
  504. ; Now we need to make the scheduler the current process.
  505.  
  506.     ADDR    CURRENT_CONTEXT_ID, D0
  507.     CLR.W    A
  508.     MOVE.B    A, @D0    ; Make scheduler the current context.
  509.  
  510.  
  511.  
  512. ; ****************************************************************************
  513. ; NOTES for SCHEDULER:    
  514.  
  515. ;            R0    ; Index into RUN_LIST (slot #), numbered 0 through N-1.
  516. ;         R1    ; Copy of the ID of the new process to be run.
  517. ;        R2    ;
  518. ;        R3    ;
  519. ;               R4    ;
  520.  
  521. ;        D0    ; 
  522. ;        D1    ; Address Pointer into RUN_LIST.
  523.  
  524. ;        B    ; Contains a local copy of the CUR_TIME variable. 
  525. ;        D    ; Number of process slots in RUN_LIST.
  526.  
  527. ; Now we need to start up the scheduler.  The scheduler will step down the run
  528. ; list, running each process as it comes due.  The process header
  529. ; will initially point to the init code for the process.  At the end of the 
  530. ; initialization phase, the user may mofify the process header to 
  531. ; point at the body of the process, where the real work is done.
  532.  
  533.     CALL    GET_TICKS    ; Get the time into C.13.
  534.     MOVE.P2    ^d1, A        ; Process ID.
  535.     CALL    ADD_PROCESS    ; Schedule process1 for immediate execution.
  536.  
  537.     MOVE.P2    RUN_LIST_SIZE, A    ; Number of process slots in RUN_LIST.
  538.     DEC.B    A        ; Decrement this number by 1; 0 to (N-1) limit.
  539.     MOVE.B    A, C        ;
  540.     MOVE.B    C, D        ; Keep RUN_LIST_SIZE in D.
  541.     CLR.W    A        ; 
  542.     MOVE.W    A, R0        ; R0 holds current slot pointer.
  543.     ADDR    RUN_LIST, C    ; Get the address of RUN_LIST into C. 
  544.     SWAP    C, D1        ; Initialize RUN_LIST index to top of RUN_LIST.
  545.  
  546.     ADDR    CUR_TIME, D0    ; Put the address of the CUR_TIME variable
  547.                 ; in D0.  Trashes A.
  548.     CALL    GET_TICKS    ; Puts the value of ticks in C.13. Trashes A.
  549.     MOVE.W    C, @D0        ; Update the value of the CUR_TIME variable.
  550.     MOVE.W    C, B        ; Put a copy in B.  This is the local 
  551.                 ; copy for the scheduler.    
  552.  
  553.     
  554. ; Now we need to step down the run list, checking the run time of each slot 
  555. ; which has a nonzero ID against the current time.  I will keep the run_list
  556. ; slot count in R0.  Whenever the scheduler is entered from the SCHEDULER entry
  557. ; point, the scheduler will pick up at the point in the run list where it left
  558. ; off.  For example, if the scheduler runs the process in slot #5, then when
  559. ; that process completes and returns to the SCHEDULER entry point, the next
  560. ; slot to be checked against the clock will be slot #6.  
  561.  
  562. SCHEDULER_CODE:
  563. ; This is the loop that scans the run list and runs the processes.
  564. ; This is the point where execution will return when a process completes.
  565.  
  566. ; First, the scheduler needs to capture the current time, which will be used 
  567. ; to determine whether a given process is due.
  568.  
  569.  
  570.     CLR.W    A        ; Clear upper bits of A.
  571. SCHED:    MOVE.B    @D1, A        ; Get the process ID of this slot.
  572.     BRNZ.B    A, GOOD_ID    ; Is this a real process ID or is it zero?
  573.     CALL    NEXT_SLOT    ; Else point to the next slot in the RUN_LIST.
  574.     JUMP    SCHEDULER_CODE    ; Keep scanning the RUN_LIST for due processes.
  575.  
  576. GOOD_ID:    ; Check the time and run the process if it is due.
  577.  
  578.     ADD.A    ^d2, D1        ; Point D1 at the execution time of this slot.
  579.     MOVE.W    @D1, C        ; Get the execution time for this process.
  580.     SUB.A    ^d2, D1        ; Point back at ID; Required by NEXT_SLOT.
  581.     BRGE.W    B, C, RUN_IT    ; Run the process if it has come due.
  582.                 ; The process ID is in A.B.
  583.     CALL    NEXT_SLOT    ; Point to the next slot in the RUN_LIST.
  584.     JUMP    SCHEDULER_CODE    ; Keep scanning the RUN_LIST for due processes.
  585.  
  586. ; ****************************************************************************
  587.  
  588. NEXT_SLOT:    ; Point to the next process in the RUN_LIST.
  589.  
  590. ; This is the routine that advances the RUN_LIST slot counter (R0) and address
  591. ; pointer (D1) to the next slot in a wraparound fashion. 
  592.  
  593.     ADD.A    2, D1    ; 
  594.     ADD.A    16, D1    ; Point D1 to the next ID in the RUN_LIST.
  595.         MOVE.B    R0, A   ; Get the RUN_LIST slot counter.
  596.     INC.B    A    ; Indicate the next process slot.
  597.     MOVE.B    A, R0    ; Update R0 with the new slot count.
  598.     MOVE.B    D, C    ; Get the RUN_LIST index limit.
  599.     BRGE.B    C, A, DONE_NEXT_SLOT    ; If not end of list, then return.
  600.  
  601. ; We have reached the end of the RUN_LIST, so we need to reset the RUN_LIST
  602. ; slot counter in R0 and the memory pointer in D1.
  603.  
  604.     CALL    GET_TICKS    ; Puts the value of ticks in C.13. Trashes A.
  605.     MOVE.W    C, B        ; Put a copy in B.  This is the local 
  606.                 ; copy for the scheduler.    
  607.     CLR.B    A
  608.     MOVE.B    A, R0    ; Clear out the slot counter in R0.
  609.     ADDR    RUN_LIST, C    ; Get the address of RUN_LIST into C. 
  610.     SWAP    C, D1        ; Initialize RUN_LIST index to top of RUN_LIST.
  611. DONE_NEXT_SLOT:
  612.     RETCLRC    
  613.  
  614. ; ****************************************************************************
  615. RUN_IT:        ; Run the process pointed to by A.B.
  616.  
  617. ; This is the routine that runs the process pointed to by A.B.  This is done
  618. ; by getting the process header pointer from the process descriptor list. 
  619. ; The process header contains pointers to the process context and to the code
  620. ; for the process.  The process context is first restored, and then execution 
  621. ; jumps to the process code.
  622.  
  623.     MOVE.B    A, R1        ; Save the new process ID for now.
  624.  
  625.     ADDR    CUR_TIME, D0    ; Put the address of the CUR_TIME variable
  626.                 ; in D0.  Trashes A.
  627.     MOVE.W    B, C        ; Time that was compared to time stamp.
  628.     MOVE.W    C, @D0        ; Update the value of the CUR_TIME variable.
  629.  
  630.     CLR.W    A
  631.     MOVE.B    A, @D1        ; Write a zero over the process ID in the 
  632.                 ; RUN_LIST, thus deleting the process from the
  633.                 ; RUN_LIST.
  634.     CALL    NEXT_SLOT    ; Point SCHEDULER to next slot in the RUN_LIST.
  635.     CALL    SAVE_CONTEXT    ; Uses CURRENT_CONTEXT_ID as a process ID to 
  636.                 ; save the current register context to the 
  637.                 ; appropriate place.  Does not save A or C.
  638.     ADDR     CURRENT_CONTEXT_ID, D0    ; Point D0 at curr context variable.
  639.                     ; Trashes A.
  640.     MOVE.B    R1, A        ; Restore the new process ID.
  641.     MOVE.B    A, @D0        ; Store the new process ID into the 
  642.                 ; CURRENT_CONTEXT_ID variable.
  643.     CALL    RESTORE_CONTEXT    ; Restores the context of the new process, and
  644.                 ; leaves a pointer to the new process code in
  645.                 ; C.A. 
  646.  
  647.  
  648.  
  649.     JUMP    C        ; Jump to new process code.
  650.  
  651. ; ****************************************************************************
  652. RESTORE_CONTEXT:
  653. ; This routine uses the context ID in A.B and restores that process context.
  654. ; The pointer to the code for the new process is left in C.A.
  655.  
  656.     CLR.W    C    ; 
  657.     MOVE.B    A, C    ;     
  658.     MOVE.W    C, A    ; Zero out upper bits of A.
  659.         ADD.A    C, C    ; 
  660.     ADD.A    C, C    ;
  661.     ADD.A    A, C    ; ...and multiply by 5.
  662.  
  663. ; C.A now contains the offset from beginning of the process descriptor list.
  664.  
  665.  
  666.     ADDR    DESCRIPTOR_LIST, D0 ; Address of descriptor list. Trashes A.
  667.     SWAP.A    A, D0
  668.     ADD.A    C, A    ; Add in the offset.  D0 points at process descriptor.
  669.     SWAP.A    A, D0
  670.     MOVE.A    @D0, C    ; Get the descriptor (pointer to the process header).
  671.  
  672.  
  673. ; Now that we have the descriptor (a pointer to the process header), we need
  674. ; to fetch the context pointer, restore the context, and then leave a pointer
  675. ; to the process code in C.A.
  676.  
  677.     MOVE.A    C, D0    ; Point D0 to the process header.
  678.     MOVE.A    @D0, A    ; Get the address of the process context.
  679.     ADD.A    ^d5, D0    ; Point D0 to the code pointer.
  680.     MOVE.A    @D0, C    ; Get the code pointer.
  681.     PUSH.A    C    ; Save the code pointer on the stack for now.
  682.     MOVE.A    A, D0    ; Point D0 to the context area. 
  683.     MOVE.W    @D0, C        ; 
  684.     MOVE.W    C, B        ; Restore B.
  685.     ADD.A    ^d16, D0
  686.     MOVE.W    @D0, C        ;
  687.     MOVE.W    C, D        ; Restore D.
  688.     ADD.A    ^d16, D0
  689.     MOVE.A    @D0, C        ; Restore D0 (in C for now).
  690.     ADD.A    ^d5, D0
  691.     SWAP.A    D1, C           ; Save D0 contents into D1 for now.
  692.     MOVE.A    @D0, C        ; Get D1 contents into C.
  693.     SWAP.A    D1, C         ; Restore D1. C holds restored D0 contents.
  694.     ADD.A    ^d5, D0
  695.     MOVE.W    @D0, A        ; 
  696.     MOVE.W    A, R0        ; Restore R0.
  697.     ADD.A    ^d16, D0
  698.     MOVE.W    @D0, A        ; 
  699.     MOVE.W    A, R1        ; Restore R1.
  700.     ADD.A    ^d16, D0
  701.     MOVE.W    @D0, A        ; 
  702.     MOVE.W    A, R2        ; Restore R2.
  703.     ADD.A    ^d16, D0
  704.     MOVE.W    @D0, A        ; 
  705.     MOVE.W    A, R3        ; Restore R3.
  706.     ADD.A    ^d16, D0
  707.     MOVE.W    @D0, A        ; 
  708.     MOVE.W    A, R4        ; Restore R4.
  709.     SWAP.A    C, D0        ; Restore D0.
  710.     POP.A    C        ; Pop the pointer to the process code in C.
  711.     RET            ; ...and return.
  712.  
  713. ; ****************************************************************************
  714.  
  715. SAVE_CONTEXT:
  716. ; This routine uses the context ID in CURRENT_CONTEXT_ID and saves that 
  717. ; process context.
  718. ; Note that this routine makes sure to not alter the states of the system 
  719. ; registers or the general purpose registers in the process of saving them.
  720. ; Note also that A and C are not saved.  The contents of B, D, D0, D1, and 
  721. ; R0-R4 are preserved by this routine.
  722.  
  723.     CALL    GET_CURRENT_ID    ; Get CURRENT_CONTEXT_ID into A.B
  724. SAVE_CONTEXT_BY_A:    ; This entry point allows the process to save the 
  725.             ; current context to that of the process given in A.B.
  726.     CLR.W    C    ; 
  727.     MOVE.B    A, C    ;     
  728.     MOVE.W    C, A    ; Zero out upper bits of A.
  729.         ADD.A    C, C    ; 
  730.     ADD.A    C, C    ;
  731.     ADD.A    A, C    ; ...and multiply by 5 to give an offset into the
  732.             ; descriptor list.
  733.  
  734. ; C.A now contains the offset from beginning of the process descriptor list.
  735.  
  736.     SWAP.A    A, D0    ; Get a copy of D0 into A.
  737.     SWAP.A    A, C    ; Do not lose the descriptor offset (save it in A).
  738.     PUSH.A    C    ; Save old D0 on the return stack.
  739.     SWAP.A    A, C    ; Restore the descriptor offset into C.
  740.     ADDR    DESCRIPTOR_LIST, D0 ; Address of descriptor list. Trashes A.
  741.     SWAP.A    A, D0    ; Put pointer to descriptor list into A.
  742.     ADD.A    C, A    ; Add in the offset.  
  743.     MOVE.A    A, D0   ; D0 points at process descriptor.    
  744.     MOVE.A    @D0, C    ; Get the descriptor (pointer to the process header).
  745.     
  746.  
  747. ; Now that we have the descriptor (a pointer to the process header), we need
  748. ; to fetch the context pointer and save the process context.
  749.  
  750.     MOVE.A    C, D0    ; Point D0 to the process header.
  751.     MOVE.A    @D0, C    ; Get the address of the context.
  752.     MOVE.A    C, D0    ; Point D0 to the context area. 
  753.     MOVE.W    B, A        ; 
  754.     MOVE.W    A, @D0        ; Save B.
  755.     ADD.A    ^d16, D0
  756.     SWAP.W    D, C
  757.     MOVE.W    C, @D0        ; Save D.
  758.     SWAP.W    D, C
  759.     ADD.A    ^d16, D0
  760.     POP.A    C        ; Recover the old D0 (pushed previously).
  761.     MOVE.A    C, @D0        ; Save D0. Leave copy of D0 in C.
  762.     ADD.A    ^d5, D0
  763.     SWAP.A    D1, C        ; 
  764.     MOVE.A    C, @D0        ; Save D1.
  765.     SWAP.A    D1, C         ; Restore D1. copy of old D0 still in C.
  766.     ADD.A    ^d5, D0
  767.     MOVE.W    R0, A        ; 
  768.     MOVE.W    A, @D0        ; Save R0.
  769.     ADD.A    ^d16, D0
  770.     MOVE.W    R1, A        ; 
  771.     MOVE.W    A, @D0        ; Save R1.
  772.     ADD.A    ^d16, D0
  773.     MOVE.W    R2, A        ; 
  774.     MOVE.W    A, @D0        ; Save R2.
  775.     ADD.A    ^d16, D0
  776.     MOVE.W    R3, A        ; 
  777.     MOVE.W    A, @D0        ; Save R3.
  778.     ADD.A    ^d16, D0
  779.     MOVE.W    R4, A        ; 
  780.     MOVE.W    A, @D0        ; Save R4.
  781.     SWAP.A    C, D0        ; Restore D0.
  782.     RET            ; Clear carry bit and return.
  783.  
  784. ; ****************************************************************************
  785.  
  786. ADD_PROCESS:
  787. ; This routine will add a process to the process list.  The basic method used
  788. ; is simply to insert the new process ID and execution time into the first 
  789. ; empty slot (defined as one which has a zero for its process ID).
  790. ; The process ID is expected in A.B, and the process execution time is expected
  791. ; in C.13.
  792. ;
  793. ; Note: This routine changes many of the system registers, as well as the 
  794. ;    general purpose registers.  For this reason, it is highly recommended
  795. ;    that the calling routine do a SAVE_CONTEXT before executing this 
  796. ;    routine, followed immediately by a RESTORE_CONTEXT or a jump to the
  797. ;    scheduler.
  798.  
  799.     MOVE.B    A, R2        ; Save the new process ID.
  800.     BRZ.B    A, BAD_PROC_ID    ; Zero is not a valid process ID.
  801.     MOVE.W    C, R3        ; Save the execution time of the new process.
  802.     MOVE.P2    NUM_OF_PROC, C    ; Get the number of defined processes.
  803.     BRGE.B    C, A, J_VPI ; Did the user pass in a valid ID?
  804.     JUMP    BAD_PROC_ID    ; If not then output the error information.
  805. J_VPI:    JUMP    VALID_PROC_ID   ; Else schedule the process.
  806. BAD_PROC_ID:     CALL    RESTORE_REGISTERS
  807.     MOVE.P5    ^d98, C
  808.     MOVE.A    C, R0
  809.     CALL    PUSH_R0_SHORT    ; Push an error code of 98 to indicate that
  810.                 ; a process tried to schedule an invalid ID.
  811.     CALL    SAVE_REGISTERS
  812.  
  813.     ADDR    CURRENT_CONTEXT_ID, D0
  814.     CLR.W    C
  815.     MOVE.B    @D0, C
  816.     MOVE.W    C, R0               ; Push the ID of the process which 
  817.     CALL    RESTORE_REGISTERS    ; caused the error.
  818.     CALL    PUSH_R0_SHORT    
  819.     CALL    SAVE_REGISTERS
  820.     
  821.  
  822.     CLR.W    A
  823.     MOVE.B    R2, A
  824.     MOVE.A    A, R0
  825.     CALL    RESTORE_REGISTERS
  826.     CALL    PUSH_R0_SHORT   ; Push the erroneous ID that the current
  827.     CALL    SAVE_REGISTERS  ; process tried to schedule.
  828.     JUMP    RR_RPLCONT    
  829.     
  830. VALID_PROC_ID:
  831.     CLR.W    C    
  832.     MOVE.P2    RUN_LIST_SIZE, C    ; Number of process slots in RUN_LIST.
  833.     DEC.B    C        ; Decrement this number by 1; 0 to (N-1) limit.
  834.     MOVE.W    C, D        ; Keep RUN_LIST_SIZE limit in D.
  835.     CLR.W    A        ; 
  836.     MOVE.W    A, R0        ; R0 holds current slot pointer.
  837.     ADDR    RUN_LIST, C    ; Get the address of RUN_LIST into C. 
  838.     SWAP.A    C, D1        ; Initialize RUN_LIST index to top of RUN_LIST.
  839.  
  840. ADD_PROCESS_LOOP:
  841.     MOVE.B    @D1, A        ; Check for an empty slot.
  842.     BRZ.B    A, FOUND_EMPTY    ; 
  843.     CALL    NEXT_SLOT    ; Look at the next slot.
  844.     MOVE.B    R0, A        ; What slot are we looking at?
  845.     BRNZ.B    A, ADD_PROCESS_LOOP ; Fall through the loop when every slot
  846.                 ; is non-empty.
  847. ; If we fall through this loop, then we are in a serious error scenario, 
  848. ; because the process table has filled up and there are no empty slots.
  849. ; This is very bad.
  850.     move.p5    ^d99, a        ; Error code for run_list overflow.
  851. ERROR:    MOVE.A    A, R0
  852.     CALL    RESTORE_REGISTERS
  853.     CALL    PUSH_R0_SHORT     ; Push ^d99 error code to indicate a run-list
  854.     CALL    SAVE_REGISTERS    ; overflow.
  855.  
  856.     ADDR    CURRENT_CONTEXT_ID, D0
  857.     CLR.W    C
  858.     MOVE.B    @D0, C
  859.     MOVE.W    C, R0               ; Push the ID of the process which 
  860.     CALL    RESTORE_REGISTERS    ; caused the error.
  861.     CALL    PUSH_R0_SHORT    
  862.     CALL    SAVE_REGISTERS
  863.  
  864.     JUMP    RR_RPLCONT    ; Push the error code onto the stack and
  865.                 ; kill the program.
  866.  
  867.  
  868. FOUND_EMPTY:    ; We have found an empty slot!  D1 should be pointing to the
  869.         ; empty slot, so all we have to do is fill it in.
  870.     MOVE.B    R2, A    ; Get the process ID.
  871.     MOVE.B    A, @D1    ; Write the ID into the slot.
  872.     ADD.A    ^d2, D1    ; Point D1 at the execution time.
  873.     MOVE.W    R3, C    ; Get the process execution time.
  874.     MOVE.W    C, @D1    ; Write the execution time into the slot.
  875.     RET        
  876.  
  877. ; ****************************************************************************
  878.  
  879. TO_SCHEDULER:
  880. ; This routine is used by processes to tell the system to swap back to the 
  881. ; scheduler.  This routine just restores the scheduler context and jumps to 
  882. ; the scheduler.  It is up to each process to save its own context.
  883.  
  884.     ADDR    CURRENT_CONTEXT_ID, D0
  885.     CLR.W    A
  886.     MOVE.B    A, @D0    ; Make scheduler the current context.
  887.     CALL    RESTORE_CONTEXT    ; Restore the context of the scheduler.
  888.     JUMP    C    ; Jump to the scheduler. 
  889.     
  890.     
  891. ; ****************************************************************************
  892. SAVE_STATE: SWAP C, D0        ; Save D0 in C for now.  Trashes C. 
  893.     ADDR    STATE_DATA1, D0    ; Point to STATE_DATA. Trashes A. 
  894.     MOVE.W    B, A        ; 
  895.     MOVE.W    A, @D0        ; Save B.
  896.     ADD    16, D0
  897.     SWAP.W    D, C
  898.     MOVE.W    C, A        ; 
  899.     SWAP.W    D, C
  900.     MOVE.W    A, @D0        ; Save D.
  901.     ADD    16, D0
  902.     MOVE.A    C, @D0        ; Save D0 (copied to C previously).
  903.     ADD    5, D0
  904.     SWAP    D1, C
  905.     MOVE.A    C, @D0        ; Save D1.
  906.     SWAP    D1, C         ; Restore D1.
  907.     SWAP    C, D0        ; Restore D0.
  908.     RETCLRC            ; Clear carry bit and return.
  909.     
  910. RESTORE_STATE:
  911.     ADDR    STATE_DATA1, D0    ; Point to STATE_DATA. Trashes A.
  912.     MOVE.W    C, A        ; Save C in A for now.
  913.     MOVE.W    @D0, C        ; 
  914.     MOVE.W    C, B        ; Restore B.
  915.     ADD    16, D0
  916.     MOVE.W    @D0, C        ; Restore D.
  917.     MOVE.W    C, D        ; Restore B.
  918.     ADD    16, D0
  919.     MOVE.A    @D0, C        ; Restore D0 (in C for now).
  920.     ADD    5, D0
  921.     SWAP.A    D1, C           ; Save D0 contents into D1 for now.
  922.     MOVE.A    @D0, C        ; Get D1 contents into C.
  923.     SWAP    D1, C         ; Restore D1.
  924.     SWAP    C, D0        ; Restore D0.
  925.     MOVE.W    A, C        ; Preserve the value of C saved at top.
  926.     RETCLRC            ; Clear carry bit and return.
  927.     
  928. GET_TICKS:
  929.     CALL    SAVE_STATE    ; Save system registers.
  930.     CALL    ROM_GET_TICKS   ; Get the ticks value from hardware timer.
  931.     CALL    RESTORE_STATE   ; Restore system registers.  Trashes A.
  932.     RET            ; Ticks is returned in C.13.
  933.  
  934.  
  935. GET_CURRENT_ID:
  936.     ADDR    CURRENT_CONTEXT_ID, C
  937.     SWAP.A    C, D0        ; Point D0 at CURRENT_CONTEXT_ID.
  938.     MOVE.B    @D0, A
  939.     SWAP.A    C, D0        ; Restore D0.    
  940.     RET
  941.  
  942. GET_CUR_TIME:
  943.     ADDR    CUR_TIME, C
  944.     SWAP.A    C, D0        ; Point D0 at CUR_TIME.
  945.     MOVE.W    @D0, A
  946.     SWAP.A    C, D0        ; Restore D0.    
  947.     RET
  948.  
  949. ; ****************************************************************************
  950. ; This is where the definition of the multiprocessing environment ends.
  951. ; ****************************************************************************
  952.  
  953.  
  954. ; ****************************************************************************
  955. ; ****************************************************************************
  956. ; THE PROCESS DEFINITIONS START HERE.
  957. ; The code below comprises the process definitions for 7 processes.  Refer to 
  958. ; the MPE users guide for an explanation of what each process does, and how 
  959. ; the processes interact.
  960. ;
  961.  
  962.   
  963. PROCESS1_DATA:
  964. info:    data.a    ^xAAAAA ; This is a simple data pattern for process 1.
  965.     data.a    ^xAAAAA
  966.     data.a    ^xAAAAA
  967.     data.1    ^xA    ; This is broken up for users who lack GNU-C version
  968.             ; of star.
  969.  
  970. PROCESS1_INIT:
  971.  
  972.     process_start    1, process1_code ; reconfigures this process to start 
  973.                 ; at the process1_code label from now on.
  974.  
  975. ; This is process #1.  This process will kick off the keyboard scanner process
  976. ; (process #7) and then this process will perform an ongoing register integrity
  977. ; test.  This process basically implements a simple sanity check over the MPE 
  978. ; system and halts execution and pushes an error code onto the stack if it 
  979. ; detects a problem.  All this process really does is check that MPE is doing 
  980. ; the process context save/restore function properly.  This process runs rather 
  981. ; infrequently, so it does not add much overhead to the system.
  982.  
  983.  
  984.  
  985.     ADDR    CUR_TIME, D0
  986.     MOVE.W    @D0, C        ; Put CUR_TIME into C. 
  987.     MOVE.P2    ^d7, A        ; Process 7 ID. (keyboard scanner)
  988.     CALL    ADD_PROCESS     ; schedule process 7 for execution.
  989.  
  990. ; Now we need to set up the registers so that each register contains a unique
  991. ; value.  Then these values will be checked each time this process is run.
  992.  
  993.     addr    info, d0
  994.     move.w    @d0, a        ; Now A contains all As.
  995.     move.w    a, b
  996.     inc.w    a
  997.     move.w    a, c
  998.     move.w    c, d
  999.     inc.w    c
  1000.     move.a    c, d0
  1001.     inc.w    c
  1002.     move.a    c, d1
  1003.     inc.w    c
  1004.     move.w    c, r0
  1005.     inc.w    c
  1006.     move.w    c, r1
  1007.     inc.w    c
  1008.     move.w    c, r2
  1009.     inc.w    c
  1010.     move.w    c, r3
  1011.     inc.w    c
  1012.     move.w    c, r4
  1013.  
  1014.  
  1015. PROCESS1_CODE:
  1016. ; This is the point to where execution will jump when the process is run.  The
  1017. ; above initialization code is only executed the very first time this process
  1018. ; is run.
  1019.  
  1020.     ADDR    INFO, c    ; Trashes A, and C obviously.
  1021.     SWAP.A  C, D0   ; Point D0 at the data.
  1022.     MOVE.W  @D0, A
  1023.     SWAP.A  C, D0   ; Restore D0.
  1024.     MOVE.W  A, C    ; copy the data into c because only C can be compared
  1025.                     ; with D.
  1026.     BREQ.W  A, B, ok1    ; Test B
  1027.     MOVE.P5 ^D0, A    ; Error code 0 for register B. 
  1028.     JUMP    ERROUT    ;
  1029.     
  1030. OK1:    
  1031.         INC.W   C        
  1032.         BREQ.W  D, C, OK2    ; Test D.
  1033.         MOVE.P5 ^X1, A  ; Error code 1 for register D.    FAILED.
  1034.         JUMP    ERROUT  ;
  1035.         
  1036. OK2:    INC.W   A        
  1037.         INC.W   A        
  1038.         SWAP.A  C, D0    
  1039.         BREQ.A  C, A, OK3    ; Test D0.
  1040.         MOVE.P5 ^X2, A  ; Error code 2 for register D0. 
  1041.         JUMP    ERROUT  ;
  1042.          
  1043. OK3:    INC.W   A        
  1044.         SWAP.A  C, D0   ; Restore D0 from the previous test.
  1045.         SWAP.A  C, D1   ; 
  1046.         BREQ.A  C, A, OK4    ; Test D1.
  1047.         MOVE.P5 ^X3, A  ; Error code 3 for register D1.  FAILED.
  1048.         JUMP    ERROUT  ;
  1049.         
  1050. OK4:    SWAP.A  C, D1   ; Restore D1 from the previous test.
  1051.         INC.W   A        
  1052.         MOVE.W  R0, C    
  1053.         BREQ.A  C, A, OK5    ; Test r0.
  1054.         MOVE.P5 ^X4, A  ; Error code 4 for register r0. 
  1055.         JUMP    ERROUT  ;
  1056.                          
  1057. OK5:    INC.W   A        
  1058.         MOVE.W  R1, C    
  1059.         BREQ.A  C, A, OK6    ; Test r1.
  1060.         MOVE.P5 ^X5, A  ; Error code 5 for register r1. 
  1061.         JUMP    ERROUT  ;
  1062.         
  1063. OK6:    INC.W   A        
  1064.         MOVE.W  R2, C    
  1065.         BREQ.A  C, A, OK7    ; Test r2.
  1066.         MOVE.P5 ^X6, A  ; Error code 6 for register r2. 
  1067.         JUMP    ERROUT  ;
  1068.                          
  1069. OK7:    INC.W   A        
  1070.         MOVE.W  R3, C    
  1071.         BREQ.A  C, A, OK8    ; Test r3.
  1072.         MOVE.P5 ^X7, A  ; Error code 7 for register r3. 
  1073.         JUMP    ERROUT  ;
  1074.                          
  1075. OK8:    INC.W   A        
  1076.         MOVE.W  R4, C    
  1077.         BREQ.A  C, A, OK9    ; Test r4.
  1078.         MOVE.P5 ^X8, A  ; Error code 8 for register r4. 
  1079.         JUMP    ERROUT  ;
  1080.         
  1081. OK9:    
  1082.     CALL    SAVE_CONTEXT
  1083.     ADDR    CUR_TIME, d0    ; What time is it?
  1084.     MOVE.W    @D0, C
  1085.     CLR.W    A
  1086.     MOVE.P5 ^x1FFF, A
  1087.     ADD.W    A, C        ; 
  1088.     MOVE.P2    ^d2, A        ; Process 2 ID.
  1089.     CALL    ADD_PROCESS     ; schedule process 2 for execution in 1 sec.
  1090.     JUMP    TO_SCHEDULER
  1091.  
  1092. ERROUT: MOVE.A  A, R0           ; Error code was placed in A above.
  1093.         CALL    RESTORE_REgisters
  1094.         CALL    PUSH_R0_SHort   ; Push the error code for the bad register.
  1095.         CALL    SAVE_REGISters
  1096.         JUMP    RR_RPLCONT    
  1097.  
  1098.  
  1099. ; This concludes the definition of process #1.
  1100. ; ****************************************************************************
  1101. ; ****************************************************************************
  1102. ; The definition of process #2 starts here.  The purpose of this process
  1103. ; is just to clear out all of the process registers.  That way, if process
  1104. ; #1 shows that the processor registers have the correct context for that
  1105. ; process, then I know that the context save/restore by MPE was successfull.
  1106.  
  1107. PROCESS2_INIT:
  1108.  
  1109.     CLR.W    A
  1110.     MOVE.A    A, D0
  1111.     MOVE.A    A, D1
  1112.     MOVE.W    A, B
  1113.     MOVE.W    A, C
  1114.     MOVE.W    C, D
  1115.     MOVE.W    A, R0
  1116.     MOVE.W    A, R1
  1117.     MOVE.W    A, R2
  1118.     MOVE.W    A, R3
  1119.     MOVE.W    A, R4
  1120.  
  1121.     CALL    SAVE_CONTEXT
  1122.     ADDR    CUR_TIME, d0    ; What time is it?
  1123.     MOVE.W    @D0, C
  1124.     MOVE.P2    ^d1, A        ; Process 1 ID.
  1125.         CALL    ADD_PROCESS     ; schedule process1 for immediate execution.
  1126.     JUMP    TO_SCHEDULER
  1127.  
  1128. ; This concludes the definition of process #2.
  1129. ; ****************************************************************************
  1130. ; ****************************************************************************
  1131. ; The following process definition is for processes 3#, #4, and #5.  The code
  1132. ; differentiates between the three processes where necessary by using the
  1133. ; IF_PROC and ENDIF_PROC macros, which are described in the users guide.
  1134.  
  1135.  
  1136. process3_init:
  1137. process4_init:
  1138. process5_init:
  1139.  
  1140. ; ****************************************************************************
  1141. ; ****************************************************************************
  1142.  
  1143. ; This is the beginning of the bullet process.  The purpose of this
  1144. ; process is to move an "F" through screen memory one pixel at a time.  This
  1145. ; process will move the "F" one pixel to the right each time it is called.
  1146. ; At the bottom of this process, it checks to see whether it has reached the
  1147. ; end of screen memory, and if not it reschedules itself for future 
  1148. ; execution based on the time inverval specified.  This process definition 
  1149. ; differentiates between processes #3, #4, and #5 and reschedules these 
  1150. ; different processes at different intervals.  This gives different speeds of 
  1151. ; motion for the three "bullets" on the screen.
  1152.  
  1153. ; The RPL from which MPE was called clears the screen and tells the display 
  1154. ; controller to use PICT for the display (by doing a PVIEW command).
  1155.  
  1156.  
  1157. ; First I need to modify the process header of this process so that execution
  1158. ; will start at the PROCESS_CODE label in future invocations.
  1159.  
  1160.     addr    process3_code, C ; Get address of new entry point for this 
  1161.                 ; process.
  1162.     cur_process_start    ; Reconfigures the current process to start
  1163.                 ; from the entry point given in C.A.
  1164.  
  1165.     MOVE.A     PICT_POINTER, D0 ; Put the address of PICT in D0.
  1166.     MOVE.A    @D0, C        ; Get the pointer to PICT.
  1167.     ADD.A    10, C        ; Skip over prolog and size fields.
  1168.     ADD.A    10, C        ; Skip over row and column fields.
  1169.     MOVE.A    C, D        ; Save the address where the screen memory
  1170.                 ; starts.
  1171.     MOVE.P5    ^d2174, A    ; Number of nibbles - 2 in PICT.
  1172.     ADD.A    A, C        ; A = address of last-1 nibble in PICT.
  1173.     MOVE.A    C, R2        ; Save it in R2.
  1174.  
  1175.  
  1176.  
  1177. ; Now stick an "F" at the beginning of the screen memory.
  1178.  
  1179.  
  1180.     CLR.W    A        ; Clear register A.
  1181.     MOVE.B    A, B        ; Clear out the bottom byte of B (bit counter).
  1182.     MOVE.P1    ^xF, A        ; Make A.A = F.
  1183.     MOVE.A    D, C        ; Get the pointer into PICT.
  1184.     MOVE.A    C, D0        ; Point D0 at PICT.
  1185.     MOVE.1    A, @D0        ; Set the first nibble of the screen to be F.
  1186.  
  1187. PROCESS3_CODE:
  1188. PROCESS4_CODE:
  1189. PROCESS5_CODE:
  1190.  
  1191.  
  1192. ; Now what we want to do is to move the "F" one pixel at a time through the
  1193. ; screen memory.  I think I'll do it the easy way, by keeping track of the
  1194. ; counter in B.B and just doing compares on that; there are only 4 cases to
  1195. ; test.
  1196.  
  1197.  
  1198.     INC.1    B        ; Increment the bit counter to the next value.
  1199.  
  1200.     MOVE.A    D, C        ; Get pointer into PICT.
  1201.     SWAP.A    C, D1        ; Point D1 into PICT.
  1202.     CLR.B    A        ; Use A.B to test the current value of the bit
  1203.                 ; counter.
  1204.     BREQ.1    A, B, ZERO    ; 
  1205.     INC.1    A   
  1206.     BREQ.1    A, B, ONE
  1207.     INC.1    A
  1208.     BREQ.1    A, B, TWO
  1209. THREE:    MOVE.P1    ^x8, A        ; Put New bit pattern of nibble  in A.
  1210.     INC.1    P        ; 
  1211.     MOVE.P1    ^x7, A        ; Put new bit pattern of nibble+1 in A. 
  1212.     MOVE.1    0, P        ; Zero out the Pointer register.
  1213.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1214.     INC.A    D        ; Increment the PICT pointer.
  1215.     MOVE.P2    ^xF, C        ; 
  1216.     MOVE.B    C, B        ; So B resets to 0 on next iteration.
  1217.         JUMP DOIT 
  1218. ZERO:    
  1219.     MOVE.A    D, C        ; Get the pointer into PICT.
  1220.     DEC.A    C        ; point to the nibble we just left.
  1221.     CLR.B    A    
  1222.     SWAP    C, D1        ; Point D1 to the old PICT nibble,
  1223.     MOVE.1    A, @D1        ; and clear it.
  1224.     SWAP    C, D1        ; Restore the PICT pointer in D1.
  1225.     MOVE.P1    ^xF, A        ; Put New bit pattern of nibble  in A.
  1226.     INC.1    P        ; 
  1227.     MOVE.P1    ^x0, A        ; Put new bit pattern of nibble+1 in A. 
  1228.     MOVE.1    0, P        ; Zero out the Pointer register.
  1229.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1230.         JUMP DOIT 
  1231. ONE:    MOVE.P1    ^xE, A        ; Put New bit pattern of nibble  in A.
  1232.     INC.1    P        ; 
  1233.     MOVE.P1    ^x1, A        ; Put new bit pattern of nibble+1 in A. 
  1234.     MOVE.1    0, P        ; Zero out the Pointer register.
  1235.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1236.         JUMP DOIT
  1237. TWO:     MOVE.P1    ^xC, A        ; Put New bit pattern of nibble  in A.
  1238.     INC.1    P        ; 
  1239.     MOVE.P1    ^x3, A        ; Put new bit pattern of nibble+1 in A. 
  1240.     MOVE.1    0, P        ; Zero out the Pointer register.
  1241.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1242.         
  1243. DOIT:    
  1244.     MOVE.A    R2, C        ; Get the address of the last nibble in PICT.    
  1245.     BRLT.A    D, C, CONTINUE    ; If we havent written the last location, then
  1246.                 ; we should reschedule this process for future
  1247.                 ; execution.
  1248.     JUMP    rr_rplcont    ; Else, we can just exit.
  1249.  
  1250.  
  1251. CONTINUE: 
  1252.  
  1253.     CALL    SAVE_CONTEXT    ; Save the context for this process.
  1254.  
  1255.     ADDR    CUR_TIME, D0    ; Get the current run-time.
  1256.     MOVE.W    @D0, C
  1257.     MOVE.W    C, R0        ; Save it in R0 for now.
  1258.  
  1259.     IF_PROC 3
  1260.       CLR.W    A
  1261.       MOVE.P5 ^x11F, A    ; Reschedule interval for process 3.
  1262.       JUMP    PROCESS3_CONT2
  1263.     ENDIF_PROC
  1264.  
  1265.     IF_PROC    4
  1266.       CLR.W    A
  1267.       MOVE.P5 ^x9F, A    ; Reschedule interval for process 4.
  1268.       JUMP    PROCESS3_CONT2
  1269.     ENDIF_PROC
  1270.  
  1271.     IF_PROC    5
  1272.       CLR.W    A
  1273.       MOVE.P5 ^xDF, A    ; Reschedule interval for process 5.
  1274.       JUMP    PROCESS3_CONT2
  1275.     ENDIF_PROC
  1276.  
  1277. PROCESS3_CONT2:    MOVE.W    R0, C           ; The current run-time.
  1278.     ADD.W    A, C        ; Next time that the hyphen should move.
  1279.     MOVE.W    C, R0        ; Save it in R0.
  1280.  
  1281.     CALL    GET_CURRENT_ID    ; Get process ID into A.B. Trashes C.
  1282.  
  1283.     MOVE.W    R0, C        ; Next time process should be run.
  1284.     CALL    ADD_PROCESS    ; Schedule this process to run at the
  1285.                 ; time given in C.A.     
  1286.  
  1287.     JUMP    TO_SCHEDULER    ; Go back to the scheduler.
  1288.  
  1289.  
  1290. ; The process definition for processes #3, #4, and #5 ends here.
  1291. ; *************************************************************************
  1292. ; *************************************************************************
  1293.  
  1294. ; The definition of process #6 starts here.  This process is very similar to 
  1295. ; the above definition for processes #3, #4, and #5.  Like the above
  1296. ; definition, process #6 will also move a "bullet" through screen memory.  The 
  1297. ; difference is that this process will schedule itself based on absolute time, 
  1298. ; using the hardware timer to keep track of where the "bullet" should be on the
  1299. ; screen, and then catching itself up to that point.  In this manner, this 
  1300. ; process will not slow down as the system becomes heavily loaded, but rather
  1301. ; it will use a constant amount of CPU time on the average, regardless of how
  1302. ; many processes are running on the system. 
  1303.  
  1304. PROCESS6_INIT:
  1305.     process_start    6, PROCESS6_CODE
  1306.  
  1307.  
  1308.     MOVE.A     PICT_POINTER, D0 ; Put the address of PICT in D0.
  1309.     MOVE.A    @D0, C        ; Get the pointer to PICT.
  1310.     ADD.A    10, C        ; Skip over prolog and size fields.
  1311.     ADD.A    10, C        ; Skip over row and column fields.
  1312.     MOVE.A    C, D        ; Save the address where the screen memory
  1313.                 ; starts.
  1314.     MOVE.P5    ^d2174, A    ; Number of nibbles - 2 in PICT.
  1315.     ADD.A    A, C        ; A = address of last-1 nibble in PICT.
  1316.     MOVE.A    C, R2        ; Save it in R2.
  1317.  
  1318.     CLR.W    A
  1319.     MOVE.W    A, R0
  1320.     ADDR    CUR_TIME, D0
  1321.     MOVE.W    @D0, C
  1322.     MOVE.W    C, R0         ; Save time of first invocation of process.
  1323.  
  1324.  
  1325. ; Now stick an "F" at the beginning of the screen memory.
  1326.  
  1327.  
  1328.     CLR.W    A        ; Clear register A.
  1329.     MOVE.B    A, B        ; Clear out the bottom byte of B (bit counter).
  1330.     MOVE.P1    ^xF, A        ; Make A.A = F.
  1331.     MOVE.A    D, C        ; Get the pointer into PICT.
  1332.     MOVE.A    C, D0        ; Point D0 at PICT.
  1333.     MOVE.1    A, @D0        ; Set the first nibble of the screen to be F.
  1334.  
  1335. PROCESS6_CODE:
  1336.  
  1337.  
  1338. ; Now what we want to do is to move the "F" one pixel at a time through the
  1339. ; screen memory.  I think I'll do it the easy way, by keeping track of the
  1340. ; counter in B.B and just doing compares on that; there are only 4 cases to
  1341. ; test.
  1342.  
  1343. PROCESS6_LOOP:
  1344.  
  1345.     INC.1    B        ; Increment the bit counter to the next value.
  1346.  
  1347.     MOVE.A    D, C        ; Get pointer into PICT.
  1348.     SWAP.A    C, D1        ; Point D1 into PICT.
  1349.     CLR.B    A        ; Use A.B to test the current value of the bit
  1350.                 ; counter.
  1351.     BREQ.1    A, B, process6_ZERO    ; 
  1352.     INC.1    A   
  1353.     BREQ.1    A, B, process6_ONE
  1354.     INC.1    A
  1355.     BREQ.1    A, B, process6_TWO
  1356. PROCESS6_THREE:    MOVE.P1    ^x8, A        ; Put New bit pattern of nibble  in A.
  1357.     INC.1    P        ; 
  1358.     MOVE.P1    ^x7, A        ; Put new bit pattern of nibble+1 in A. 
  1359.     MOVE.1    0, P        ; Zero out the Pointer register.
  1360.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1361.     INC.A    D        ; Increment the PICT pointer.
  1362.     MOVE.P2    ^xF, C        ; 
  1363.     MOVE.B    C, B        ; So B resets to 0 on next iteration.
  1364.         JUMP process6_DOIT 
  1365. PROCESS6_ZERO:    
  1366.     MOVE.A    D, C        ; Get the pointer into PICT.
  1367.     DEC.A    C        ; point to the nibble we just left.
  1368.     CLR.B    A    
  1369.     SWAP    C, D1        ; Point D1 to the old PICT nibble,
  1370.     MOVE.1    A, @D1        ; and clear it.
  1371.     SWAP    C, D1        ; Restore the PICT pointer in D1.
  1372.     MOVE.P1    ^xF, A        ; Put New bit pattern of nibble  in A.
  1373.     INC.1    P        ; 
  1374.     MOVE.P1    ^x0, A        ; Put new bit pattern of nibble+1 in A. 
  1375.     MOVE.1    0, P        ; Zero out the Pointer register.
  1376.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1377.         JUMP process6_DOIT 
  1378. PROCESS6_ONE:    MOVE.P1    ^xE, A        ; Put New bit pattern of nibble  in A.
  1379.     INC.1    P        ; 
  1380.     MOVE.P1    ^x1, A        ; Put new bit pattern of nibble+1 in A. 
  1381.     MOVE.1    0, P        ; Zero out the Pointer register.
  1382.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1383.         JUMP process6_DOIT
  1384. PROCESS6_TWO:     MOVE.P1    ^xC, A        ; Put New bit pattern of nibble  in A.
  1385.     INC.1    P        ; 
  1386.     MOVE.P1    ^x3, A        ; Put new bit pattern of nibble+1 in A. 
  1387.     MOVE.1    0, P        ; Zero out the Pointer register.
  1388.     MOVE.2    A, @D1        ; Write out the two data values to the display.
  1389.         
  1390. PROCESS6_DOIT:    
  1391.     MOVE.A    R2, C        ; Get the address of the last nibble in PICT.    
  1392.     BRLT.A    D, C, PROCESS6_CONTINUE    
  1393.                 ; If we havent written the last location, then
  1394.                 ; we should reschedule this process for future
  1395.                 ; execution.
  1396.     JUMP    rr_rplcont    ; Else, we can just exit.
  1397.  
  1398.  
  1399. PROCESS6_CONTINUE: 
  1400.  
  1401.     CLR.W    A
  1402.     MOVE.P5    ^x9F, A  
  1403.     MOVE.W    R0, C           ; The last scheduled run-time.
  1404.     ADD.W    C, A        ; Next time that the hyphen should move.
  1405.     MOVE.W    A, R0           ; Save next run-time.
  1406.     CALL    GET_TICKS    ; Current time.
  1407.     MOVE.W    R0, A        ; Get next run-time.
  1408.     BRGE.W    C, A, PROCESS6_LOOPER    ; Run it again if it is time.
  1409.  
  1410.     CALL    SAVE_CONTEXT    ; Save the context for this process.
  1411.  
  1412.  
  1413.     MOVE.W    R0, C        ; Next time process should be run.
  1414.     CLR.W    A
  1415.     MOVE.P2    ^d6, A        ; ID for this process into A.B.
  1416.     CALL    ADD_PROCESS    ; Schedule this process to run at the
  1417.                 ; time given in C.     
  1418.     JUMP    TO_SCHEDULER    ; Go back to the scheduler.
  1419.  
  1420.  
  1421. PROCESS6_LOOPER: JUMP    PROCESS6_LOOP
  1422.  
  1423. ;**************************************************************************
  1424. ;**************************************************************************
  1425. ; This process is responsible for reading the keyboad, and spawning a new
  1426. ; processes whenever the [1] key is pressed.  All other key presses are 
  1427. ; discarded without action.
  1428. ; This process will schedule processes #3, #4, #5, and #6 in that sequence.  
  1429. ; One processes will be spawned for each press of the [1] key.  This process
  1430. ; does one scan of the key buffer on each invocation.  After process #6 is 
  1431. ; scheduled, this process ceases to reschedule.
  1432.  
  1433. PROCESS7_INIT:
  1434.     process_start 7, process7_code
  1435.         move.p2    ^d2, C        ; Pointer to the next-1 bullet process.
  1436.     MOVE.B    C, B            ; Save it in B.
  1437.     MOVE.P2    ^d6, C          ; Upper process ID limit.
  1438.     MOVE.B    C, D            ; Save it in D.
  1439.  
  1440. PROCESS7_CODE:
  1441.     call    kb_poll            ; Get key, if any
  1442.     brcs    process7_check_key
  1443.     CALL    SAVE_CONTEXT
  1444.     jump    process7_sched          ; If not then reschedule.
  1445.  
  1446. PROCESS7_CHECK_KEY:
  1447.     move.p2    ^d49, C            ; Scan code for the [+] key.
  1448.     breq.b    c, a, process7_valid_key    ; Did user press [+]?
  1449.     MOVE.P2    ^d47, C
  1450.     BREQ.B    C, A, PROCESS7_ABORT
  1451.     CALL    SAVE_CONTEXT
  1452.     jump    process7_sched          ; If not then reschedule.
  1453.  
  1454. PROCESS7_ABORT:
  1455. ; The user has pressed the [.] key, so we want to abort the application.
  1456.     JUMP    RR_RPLCONT    ; This kills the application and continues
  1457.                 ; with the RPL thread.
  1458.  
  1459. PROCESS7_VALID_KEY:            ; The user pressed the [+] key!
  1460.     INC.B    B            ; Point to the next bullet ID.
  1461.     MOVE.B    B, A            ; Next process ID to start.
  1462.     MOVE.B    A, R0                   ; Save it in R0 for later (see below).
  1463.     MOVE.B    D, C                    ; Upper process ID limit.
  1464.     BRGE.B    C, A, PROCESS7_SPAWN    ; If process number <=6 then run it.
  1465.     DEC.B    B            ; Else un-increment B,
  1466.     CALL    SAVE_CONTEXT        ; save the process context and 
  1467.     JUMP    PROCESS7_SCHED        ; reschedule the process.
  1468.  
  1469. PROCESS7_SPAWN:
  1470.     CALL    SAVE_CONTEXT
  1471.     ADDR    CUR_TIME, D0        ; Trashes A.
  1472.     MOVE.B    R0, A
  1473.     MOVE.W    @D0, C
  1474.     CALL    ADD_PROCESS
  1475.  
  1476. PROCESS7_SCHED:
  1477. ; The current context of this process should have been saved by now. 
  1478.  
  1479.     ADDR    CUR_TIME, D0    ; Point to the CUR_TIME variable.
  1480.     MOVE.W    @D0, C        ; Get the current time.
  1481.     CLR.W    A
  1482.     MOVE.P3    ^x3FF, A    ; Reschedule this process to look at the
  1483.                 ; keyboard roughly 8 times per second.
  1484.     ADD.W    A, C    
  1485.     MOVE.P2    ^d7, A        ; ID for this process.
  1486.     CALL    ADD_PROCESS
  1487.  
  1488. PROCESS7_EXIT:
  1489.     JUMP    TO_SCHEDULER    ; Return control to the scheduler.
  1490.  
  1491.  
  1492. ;; Poll keyboard. System-based version. Returns the scan code in A.A.
  1493. ;; Trashes C.A, B.B, and D0. The C bit is set if a key was pressed,
  1494. ;; otherwise it's cleared.  This keyboard polling software was written
  1495. ;; by Jan Brittenson (J.E.)
  1496. ;;
  1497. ;; This is a mutation of the ROM prefixed machine code routine to
  1498. ;; get/peek the next entry in the keyboard buffer.
  1499.  
  1500.  
  1501. kb_poll:
  1502.     move.5    keybuf+1, d0    ; KB Put ptr
  1503.     move.s    @d0, a        ; A.S = put ctr
  1504.     dec    d0
  1505.     move.s    @d0, c        ; C.S = get ctr
  1506.     breq.s    c, a, $100    ; Ctrs are equal - buffer empty
  1507.  
  1508.     move    c.15, p        ; P = get ctr
  1509.     inc.s    c        ; Remove key
  1510.     move.s    c, @d0
  1511.  
  1512.     swap    c, d0
  1513.     add    p+1, c
  1514.     add    p+1, c        ; C += get ctr, in bytes
  1515.     clr    p
  1516.     move    c, d0        ; D0 = &next key
  1517.     clr.a    a
  1518.     move.b    @d0, a        ; A.A = key
  1519.     retsetc
  1520. $100:
  1521.     clr.a    a
  1522.     retclrc
  1523. ; ************************************************************************** 
  1524.  
  1525.  
  1526.     endcode
  1527.  
  1528. end
  1529.  
  1530.